;    FpuNum.s - Rotinas para conversao Asc <=> Fpu   
;    Copyright (C) 2005 - 2007 Gilmar Santos Jr (gilmarjr@dcc.ufba.br)
;
;    This program is free software; you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation; either version 2 of the License, or
;    (at your option) any later version.
;
;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License along
;    with this program; if not, write to the Free Software Foundation, Inc.,
;    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

global	_FpuToAsc
global	_AscToFpu

section .data
ten		dd	10
maxSingle	dq	1.7e+38,
			dq	-38.0,
			dq	38.0
maxDouble	dq	8.9e+307,
			dq	-307.0,
			dq	307.0

zero	dq	0.0
one		dq	1.0
halfBilion	dq	500000000.0
bilion	dq	1000000000.0
nines	dq	999999999.0
FpuTen	dq	10.0

section	.bss

signal		resb	1
saveEnv		resb	108
setEnv		resb	108
fpuCw		resw	1
precision	resd	1
notation	resb	1
sigNum		resb	1
sigExp		resb	1
digit		resd	1
decimal		resd	1
exponent	resd	1
maxNum		resq	1
minExp		resq	1
maxExp		resq	1
decLim		resd	1
lowLim		resd	1
integerCount	resd	1
integerFpu		resd	36

section	.text

; int AscToFpu([int precision],char *numAsc,int length,float *numFpu[,int *rc]);
_AscToFpu:

; +---------------------------------------------------------------------+
; | Chamada:	XCALL	_AscToFpu[,precision],numAsc,length,numFpu,rC	|
; |                                                                     |
; |		numAsc		DB	nnn DUP (?)		<--                             |
; |		precision	DD	32 ou 64 default=64	<--	                        |
; |		length		DD	nnn			<--	                                |
; |		numFpu		DD/Q	?			-->	                            |
; |		rC		DD	?			-->	                                    |
; |									                                    |
; | Retorno:	rC = 0: numFpu <-- numAsc				                |
; |		rC = 1: numAsc excede maximo				                    |
; |		rC = 2: numAsc nao numerico				                        |
; |		rC = 3: precision invalido				                        |
; +---------------------------------------------------------------------+
	push	ebp
	mov		ebp,esp
	fsave	[saveEnv]

	cmp		dword [ebp +  8],0
	je		.DoublePrecision
	mov		eax,dword [ebp +  8]		; precision

.SetPrecision:
	cmp		eax,32
	je		.SinglePrecision
	cmp		eax,64
	je		.DoublePrecision
	jmp		.InvalidPrecision

.SinglePrecision:
	mov		dword [precision], 32
	fld		qword [maxSingle]
	fstp	qword [maxNum]
	fld		qword [maxSingle +  8]
	fstp	qword [minExp]
	fld		qword [maxSingle + 16]
	fstp	qword [maxExp]
	jmp		.CaptureParameters

.DoublePrecision:
	mov		dword [precision],64
	fld		qword [maxDouble]
	fstp	qword [maxNum]
	fld		qword [maxDouble +  8]
	fstp	qword [minExp]
	fld		qword [maxDouble + 16]
	fstp	qword [maxExp]

.CaptureParameters:
	mov		ecx,dword [ebp + 16]		; length
	mov		esi,dword [ebp + 12]		; numasc


.SearchNumber:
	cmp		byte [esi],' '
	je		.NextChar
	cmp		byte [esi],9		; \t
	jne		.BeginNumber

.NextChar:
	inc		esi
	loop	.SearchNumber
	jmp		.InvalidNumber

.BeginNumber:
	fldz
	xor		ebx,ebx
	xor		edx,edx
	mov		dword [decimal],0
	mov		dword [exponent],0
	mov		byte [sigNum],'+'
	cmp		byte [esi],'+'
	je		.SignalNumber
	cmp		byte [esi],'-'
	jne		.NotSignalNumber
	mov		byte [sigNum],'-'

.SignalNumber:
	inc		esi
	dec		ecx

.NotSignalNumber:
	cmp		ecx,0
	jng		.ShortInvalidNumber
	cmp		byte [esi],','
	je		.TranslateNumber
	cmp		byte [esi],'0'
	jb		.ShortInvalidNumber
	cmp		byte [esi],'9'
	ja		.ShortInvalidNumber
	jmp		.TranslateNumber

.ShortInvalidNumber:
	jmp		.InvalidNumber

.TranslateNumber:
	cmp		byte [esi],' '
	je		.ShortInsertFpuSignal
	cmp		byte [esi],13		; cr
	je		.ShortInsertFpuSignal
	cmp		byte [esi],10		; lf
	je		.ShortInsertFpuSignal
	cmp		byte [esi],9		; \t
	je		.ShortInsertFpuSignal
	cmp		byte [esi],','
	je		.DecimalPart
	cmp		byte [esi],'e'
	je		.ShortExponentPart
	cmp		byte [esi],'E'
	je		.ShortExponentPart
	cmp		byte [esi],'0'
	jb		.ShortInvalidNumber
	cmp		byte [esi],'9'
	ja		.ShortInvalidNumber
	mov		bl,byte [esi]
	sub		bl,0x30
	movzx	eax,bl
	mov		dword [digit],eax
	fmul	qword [FpuTen]
	fiadd	dword [digit]
	fcom	qword [maxNum]
	fstsw	ax
	sahf
	ja		.ShortOutRangeNumber
	inc		esi
	loop	.TranslateNumber
.ShortInsertFpuSignal:
	jmp		.InsertFpuSignal

.ShortExponentPart:
	jmp		.ExponentPart

.ShortOutRangeNumber:
	jmp		.OutRangeNumber

.DecimalPart:
	inc		esi
	dec		ecx

.TranslateDecimal:
	cmp		byte [esi],' '
	je		.FinalDecimal
	cmp		byte [esi],13		; cr
	je		.FinalDecimal
	cmp		byte [esi],10		; lf
	je		.FinalDecimal
	cmp		byte [esi],9		; \t
	je		.FinalDecimal
	cmp		byte [esi],'e'
	je		.ShortExponentPart
	cmp		byte [esi],'E'
	je		.ShortExponentPart
	cmp		byte [esi],'0'
	jb		.ShortInvalidNumber
	cmp		byte [esi],'9'
	ja		.ShortInvalidNumber
	mov		bl,byte [esi]
	sub		bl,30h
	movzx	eax,bl
	mov		dword [digit],eax
	fmul	qword [FpuTen]
	fiadd	dword [digit]
	fcom	qword [maxNum]
	fstsw	ax
	sahf
	ja		.ShortOutRangeNumber
	inc		dword [decimal]
	inc		esi
	loop	.TranslateDecimal

.FinalDecimal:
	cmp		dword [decimal],0
	jne		.ContinuaFinalDecimal
	jmp		.InsertFpuSignal

.ContinuaFinalDecimal:
	fild	dword [decimal]

	fcomp	qword [maxExp]
	fstsw	ax
	sahf
	ja		.ShortOutRangeNumber

	mov		ecx,dword [decimal]
	fld1

.DecimalFpuMultiply:
	fmul	qword [FpuTen]
	loop	.DecimalFpuMultiply

	fdivp	st1,st0
	jmp		.InsertFpuSignal

.ExponentPart:
	inc		esi
	dec		ecx
	cmp		ecx,0
	jng		.ShortInvalidNumber

	fldz
	mov		ebx,0
	mov		edx,0

	mov		byte [sigExp],'+'
	cmp		byte [esi],'+'
	je		.SignalExponent
	cmp		byte [esi],'-'
	jne		.NotSignalExponent
	mov		byte [sigExp],'-'

.SignalExponent:
	inc		esi
	dec		ecx

.NotSignalExponent:
	cmp		ecx,0
	jng		.ShortInvalidNumber
	cmp		byte [esi],'0'
	jb		.ShortInvalidNumber
	cmp		byte [esi],'9'
	ja		.ShortInvalidNumber

.TranslateAscExponent:
	cmp		byte [esi],' '
	je 	 	.CalculateExponent
	cmp		byte [esi],13		; cr
	je 	 	.CalculateExponent
	cmp		byte [esi],10		; lf
	je 	 	.CalculateExponent
	cmp		byte [esi],9		; \t
	je 	 	.CalculateExponent
	cmp		byte [esi],'0'
	jb 	 	.ShortInvalidNumber
	cmp		byte [esi],'9'
	ja 	 	.ShortInvalidNumber
	mov		bl,byte [esi]
	sub		bl,30h
	movzx	eax,bl
	mov		dword [digit],eax
	fmul	qword [FpuTen]
	fiadd	dword [digit]
	inc		esi
	loop	.TranslateAscExponent

.CalculateExponent:
	cmp		byte [sigExp],'+'
	je		.FinalExponent
	fchs

.FinalExponent:
	fisub	dword [decimal]
	fcom	qword [minExp]
	fstsw	ax
	sahf
	jb		.ShortOutRangeNumber

	fcom	qword [maxExp]
	fstsw	ax
	sahf
	ja		.OutRangeNumber

	fistp	dword [exponent]

	cmp		dword [exponent],0
	je		.InsertFpuSignal
	mov		ecx,dword [exponent]
	fld1
	cmp		ecx,0
	jnl		.ExponentFpuMultiply
	neg		ecx

.ExponentFpuMultiply:
	fmul	qword [FpuTen]
	loop	.ExponentFpuMultiply

	cmp		dword [exponent],0
	jl		.NegativeExponent

.PositiveExponent:
	fmulp	st1,st0
	jmp		.InsertFpuSignal

.NegativeExponent:
	fdivp	st1,st0
	
.InsertFpuSignal:
	cmp		byte [sigNum],'+'
	je		.ReturnOkFpu
	fchs

.ReturnOkFpu:
	mov		edi,dword [ebp + 24]	; rc
	xor		eax,eax
	cmp		edi,0
	je		.CalcPrecision
	mov		dword [edi],eax

.CalcPrecision:
	mov		edi,dword [ebp + 20]	; numfpu
	cmp		dword [precision],32
	je		.ReturnSingleFpu

.ReturnDoubleFpu:
	fstp	qword [edi]
	jmp		.ReturnFpu

.ReturnSingleFpu:
	fstp	dword [edi]
	jmp		.ReturnFpu

.OutRangeNumber:
	mov		edi,dword [ebp + 24]	; rc
	mov		eax,1
	cmp		edi,0
	je		.ReturnFpu
	mov		dword [edi],eax
	jmp		.ReturnFpu

.InvalidNumber:
	mov		edi,dword [ebp + 24]	; rc
	mov		eax,2
	cmp		edi,0
	je		.ReturnFpu
	mov		dword [edi],eax
	jmp		.ReturnFpu

.InvalidPrecision:
	mov		edi,dword [ebp + 24]	; rc
	mov		eax,3
	cmp		edi,0
	je		.ReturnFpu
	mov		dword [edi],eax

.ReturnFpu:
	frstor	[saveEnv]
	leave
	ret

; void FpuToAsc([int precision],float *numFpu,char *numAsc,int lenght,[char notation],[int decimal]);
_FpuToAsc:

; +-------------------------------------------------------------------------------------+
; | Chamada:	XCALL	FpuToAsc[,precision],numFpu,numAsc,length[,notation][,decimal]	|
; |											|
; |		precision	DD	32 ou 64	; default=64	<--		|
; |		numFpu		DQ	?				<--		|
; |		numAsc		DB	nnn DUP (?)			-->		|
; |		length		DD	?				<--		|
; |		notation	DB	'D' ou 'E'	; default='D'	<--		|
; |		decimal		DD	?		; default=0	<--		|
; +-------------------------------------------------------------------------------------+
	push	ebp
	mov		ebp,esp
	fsave	[saveEnv]

	fstcw	word [fpuCw]
	or		word [fpuCw],0000110000000000b
	fldcw	word [fpuCw]

	fsave	[setEnv]
	frstor	[setEnv]

	mov		dword [precision],64
	cmp		dword [ebp +  8],0
	je		.ReceiveFpu

	cmp		dword [ebp + 8],64		; dword [precision]
	je		.ReceiveFpu
	mov		dword [precision],32

.ReceiveFpu:
	mov		edi,dword [ebp + 16]		; numasc
	mov		ecx,dword [ebp + 20]		; length
	mov		esi,edi
	lodsb
	cld
	rep		stosb
	dec		edi
	mov		esi,dword [ebp + 12]		; numfpu
	cmp		dword [precision],64
	je		.ReceiveDoubleFpu

.ReceiveSingleFpu:
	fld		dword [esi]
	jmp		.NumberSignal

.ReceiveDoubleFpu:
	fld		qword [esi]

.NumberSignal:
	mov		byte [sigNum],'+'
	fcom	qword [zero]
	fstsw	ax
	sahf
	jnb		.ReceiveNotation
	mov		byte [sigNum],'-'
	fchs

.ReceiveNotation:
	mov		byte [notation],'D'
	cmp		dword [ebp + 24],0
	je 	 	.ReceiveDecimal
	movzx	ebx,byte [ebp + 24]		; notation
	and		bl,11011111b
	cmp		bl,'E'
	jne		.ReceiveDecimal
	mov		byte [notation],'E'

.ReceiveDecimal:
	mov		dword [decimal],0
	mov		dword [decLim],0
	mov		dword [lowLim],0
	cmp		dword [ebp + 28],0
	je		.NormalizeFpu
	mov		eax,dword [ebp + 28]		; decimal
	mov		dword [decimal],eax
	cmp		dword [decimal],0
	jnl		.NormalizeFpu
	mov		dword [decimal],0

.NormalizeFpu:
	fcom	qword [zero]
	fstsw	ax
	sahf
	je		.ShortReturnZeroToAsc
	mov		dword [exponent],0
	jmp		.NormalizeMultiply

.ShortReturnZeroToAsc:
	jmp		.ReturnZeroToAsc

.NormalizeMultiply:
	fcom	qword [one]
	fstsw	ax
	sahf
	jnb		.NormalizeDivide
	fmul	qword [FpuTen]
	dec		dword [exponent]
	jmp		.NormalizeMultiply

.NormalizeDivide:
	fcom	qword [one]
	fstsw	ax
	sahf
	jb		.NormalizeExponent
	fdiv	qword [FpuTen]
	inc		dword [exponent]
	jmp		.NormalizeDivide

.NormalizeExponent:
	cmp		byte [notation],'D'
	je		.DecimalNotation

.ScientificNotation:
	fild	dword [decimal]
	fcomp	qword [maxExp]
	fstsw	ax
	sahf
	jna		.ExponentSignal
	fld		qword [maxExp]
	fistp	dword [decimal]

.ExponentSignal:
	mov		eax,dword [exponent]
	mov		byte [sigExp],'+'
	cmp		eax,0
	jnl		.TranslateFpuExponent
	mov		byte [sigExp],'-'
	neg		eax

.TranslateFpuExponent:
	mov		edx,0
	div		dword [ten]
	add		dl,30h
	mov		byte [edi],dl
	dec		edi
	cmp		eax,0
	jg		.TranslateFpuExponent

	mov		dl,byte [sigExp]
	mov		byte [edi],dl
	mov		byte [edi-1],'e'
	sub		edi,2
	mov		dword [exponent],0
	cmp		dword [decimal],0
	jne		.DecimalNotation
	jmp		.ReturnAsc

.DecimalNotation:
	mov		ecx,dword [decimal]
	mov		dword [decLim],ecx
	add		ecx,dword [exponent]
	cmp		ecx,0
	jng		.IntegerConvert

	fld1

	mov		dword [decimal],ecx
	fild	dword [decimal]
	fcomp	qword [maxExp]
	fstsw	ax
	sahf
	jna		.DecimalAscMultiply
	fld		qword [maxExp]
	fistp	dword [decimal]

.DecimalAscMultiply:
	fmul	qword [FpuTen]
	loop	.DecimalAscMultiply

	fmulp	st1,st0

.IntegerConvert:
	push	edi
	mov		edi,integerFpu
	mov		ecx,36
	mov		eax,0
	cld
	rep		stosd

	xor		edi,edi
	mov		dword [integerCount],1
	mov		ecx,1

.IntegerFpuDivide:
	fcom	qword [bilion]
	fstsw	ax
	sahf
	jb		.IntegerFpuExtract
	fdiv	qword [bilion]
	inc		dword [integerCount]
	inc		ecx
	jmp		.IntegerFpuDivide

.IntegerFpuExtract:
	fist	dword [integerFpu + edi]
	fmul	qword [bilion]
	fild	dword [integerFpu + edi]
	fmul	qword [bilion]
	fsubp	st1,st0
	add		edi,4
	fcom	qword [nines]
	fstsw	ax
	sahf
	jb		.LoopIntegerFpuExtract
	inc		dword [integerFpu + edi - 4]
	fsub	qword [nines]

.LoopIntegerFpuExtract:
	loop	.IntegerFpuExtractNear

	sub		edi,4
	mov		ecx,dword [integerCount]
	fcomp	qword [halfBilion]
	fstsw	ax
	sahf
	jb		.TranslateFpu
	inc		dword [integerFpu + edi]
	jmp		.TranslateFpu

.IntegerFpuExtractNear:
	jmp		.IntegerFpuExtract

.TranslateFpu:
	mov		esi,edi
	pop		edi
	mov		eax,dword [integerFpu + esi]
	mov		dword [lowLim],9
	cmp		ecx,1
	jg		.TranslateFpuDigit
	mov		ebx,dword [decLim]
	mov		dword [lowLim],ebx

.TranslateFpuDigit:
	mov		edx,0
	div		dword [ten]
	add		dl,30h
	mov		byte [edi],dl
	dec		edi
	dec		dword [lowLim]
	dec		dword [decLim]
	jnz		.NoDecimalPoint
	mov		byte [edi],','
	dec		edi

.NoDecimalPoint:
	cmp		eax,0
	jg		.TranslateFpuDigit
	cmp		dword [lowLim],0
	jg		.TranslateFpuDigit
	sub		esi,4
	mov		eax,dword [integerFpu + esi]
	mov		dword [lowLim],9
	cmp		ecx,2
	jg		.LoopTranslateFpu
	mov		ebx,dword [decLim]
	mov		dword [lowLim],ebx

.LoopTranslateFpu:
	loop	.TranslateFpuDigit

.InsertAscSignal:
	cmp		byte [notation],'E'
	je		.FinalScientificNotation
	cmp		byte [edi+1],','
	je		.InsertAscZero
	cmp		dword [decLim],0
	jg		.InsertAscZero
	cmp		byte [sigNum],'+'
	je		.ReturnAsc
	mov		byte [edi],'-'
	jmp		.ReturnAsc

.InsertAscZero:
	mov		byte [edi],'0'
	dec		edi
	dec		dword [decLim]
	jnz		.InsertAscSignal
	mov		byte [edi+1],','
	jmp		.InsertAscSignal

.FinalScientificNotation:
	mov		byte [edi],'0'
	mov		dl,byte [sigNum]
	mov		byte [edi-1],dl
	jmp		.ReturnAsc

.ReturnZeroToAsc:
   mov   al, '0'
   mov   ecx, dword [decimal]
   std
   rep   stosb
   cld
   mov   byte [edi], '0'
   cmp   dword [decimal], 0
   je    .ReturnAsc
   mov   byte [edi], ','
   mov   byte [edi-1], '0'

.ReturnAsc:
	frstor	[saveEnv]
	leave
	ret
